// size: configurable
// way: 4 (unchangable)
// entries per way: configurable

module pht #(
  parameter DEPTH = 256,
  parameter WAY = 4,
  parameter WIDTH = 2,
  parameter DEPTH_PER_WAY = DEPTH / WAY
) (
  input clk,
  input rst,

  input [$clog2(DEPTH)-1:0] waddr,
  input we,
  input taken,

  input [$clog2(DEPTH)-1:0] raddr,
  output [WAY-1:0] rdata
);

  wire [$clog2(DEPTH_PER_WAY)-1:0] addra_vec [WAY-1:0];
  wire [1:0] dina_vec [WAY-1:0];
  wire [$clog2(DEPTH_PER_WAY)-1:0] addrb_vec [WAY-1:0];
  wire we_vec [WAY-1:0];
  wire [1:0] douta_vec [WAY-1:0];
  wire [1:0] doutb_vec [WAY-1:0];

  wire [$clog2(DEPTH)-1:0] raddr_vec [WAY-1:0];
  wire [$clog2(WAY)-1:0] ridx_vec [WAY-1:0];

  wire [$clog2(WAY)-1:0] rway = raddr[$clog2(WAY)-1:0];
  wire [$clog2(WAY)-1:0] rway_vec [WAY-1:0];
  wire [$clog2(WAY)-1:0] wway = waddr[$clog2(WAY)-1:0];

  genvar way, i;
  generate
    for (i = 0; i < 4; i = i + 1) begin
      assign raddr_vec[i] = raddr + i;
      assign rway_vec[i] = rway + i;
    end
  endgenerate
  generate
    for (way = 0; way < 4; way = way + 1) begin
      assign ridx_vec[way] = way - rway;
      assign addra_vec[way] = waddr[$clog2(DEPTH)-1:$clog2(WAY)];
      assign dina_vec[way] = 
        {2{douta_vec[wway] == 2'b00}} & {
          {2{taken}} & 2'b01 |
          {2{~taken}} & 2'b00
        } |
        {2{douta_vec[wway] == 2'b01}} & {
          {2{taken}} & 2'b10 |
          {2{~taken}} & 2'b00
        } |
        {2{douta_vec[wway] == 2'b10}} & {
          {2{taken}} & 2'b11 |
          {2{~taken}} & 2'b01
        } |
        {2{douta_vec[wway] == 2'b11}} & {
          {2{taken}} & 2'b11 |
          {2{~taken}} & 2'b10
        };
      assign we_vec[way] = (way == wway) & ~rst & we;
      assign addrb_vec[way] = raddr_vec[ridx_vec[way]][$clog2(DEPTH)-1:$clog2(WAY)];

      dpdistram #(
        .WIDTH(2),
        .DEPTH(DEPTH_PER_WAY)
      ) pht_lutram (
        .clk(clk),
        .rst(rst),
        .wea(we_vec[way]),
        .ena(1'b1),
        .enb(1'b1),
        .addra(addra_vec[way]),
        .addrb(addrb_vec[way]),
        .dina(dina_vec[way]),
        .douta(douta_vec[way]),
        .doutb(doutb_vec[way])
      );
    end
  endgenerate

  assign rdata = {
    doutb_vec[rway_vec[3]][1],
    doutb_vec[rway_vec[2]][1],
    doutb_vec[rway_vec[1]][1],
    doutb_vec[rway_vec[0]][1]
  };

endmodule
